package com.cxyzy.badgeimageview

import android.content.Context
import android.content.res.Resources
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.text.TextPaint
import android.util.AttributeSet
import android.view.View
import androidx.annotation.DrawableRes
import androidx.core.content.res.ResourcesCompat
import kotlin.math.abs

/**
 * 用法：
 * 1.自定义View，在构造方法中创建Badge实例;
 * 2.重写dispatchDraw(Canvas canvas)或onDraw(Canvas canvas)方法，调用Badge的drawBadge(canvas)方法;
 * 3.添加一个getBadge()方法，返回Badge实例。
 *
 * 自定义属性：
 * badge_gravity       对齐方式
 * badge_background    角标背景
 *
 * badge_textSize      字体大小
 * badge_textColor     字体颜色
 * badge_boldText      文本加粗
 *
 * badge_offsetX       横向偏移量
 * badge_offsetY       纵向偏移量
 *
 * badge_dotRadius     小圆点半径
 * badge_padding       角标边距
 * badge_paddingLeft   角标左边距
 * badge_paddingTop    角标上边距
 * badge_paddingRight  角标右边距
 * badge_paddingBottom 角标下边距
 */
class Badge(
        /**
         * 目标View
         */
        private val mTargetView: View, attributeSet: AttributeSet?) {
    /**
     * 角标文本，为null时不显示，长度0时显示小圆点
     */
    private var mBadgeText: String? = null
    /**
     * 角标字体颜色
     */
    private var mBadgeTextColor = 0
    /**
     * 角标字体大小，默认值12dp
     */
    private var mBadgeTextSize = 0f
    /**
     * 字体是否加粗
     */
    private var mBoldTextEnable = false
    /**
     * 角标背景
     */
    private var mBadgeBackground: Drawable? = null
    /**
     * 角标内边距
     */
    private var mBadgePaddingLeft = 0f
    private var mBadgePaddingTop = 0f
    private var mBadgePaddingRight = 0f
    private var mBadgePaddingBottom = 0f
    /**
     * 角标横向偏移量
     */
    private var mBadgeOffsetX = 0f
    /**
     * 角标纵向偏移量
     */
    private var mBadgeOffsetY = 0f
    /**
     * 角标小圆点半径，默认值10dp
     */
    private var mBadgeDotRadius = 0f
    /**
     * 角标对齐方式
     */
    private var mBadgeGravity = 0
    /**
     * 文本画笔工具
     */
    private lateinit var mBadgeTextPaint: TextPaint
    /**
     * 文本画笔工具的度量
     */
    private var mBadgeTextFontMetrics: Paint.FontMetrics? = null
    /**
     * 角标背景边界值
     */
    private var mBadgeBackgroundBounds: Rect? = null
    /**
     * 文本高度
     */
    private var mBadgeTextHeight = 0f
    /**
     * 目标View的宽高值
     */
    private var mViewWidth = 0
    private var mViewHeight = 0
    /**
     * 初始化变量
     */
    private fun initBadge(context: Context, attributeSet: AttributeSet?) { //dp&sp转px的系数
        val mDensityDP = context.resources.displayMetrics.density
        val mDensitySP = context.resources.displayMetrics.scaledDensity
        val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.Badge)
        //角标对齐方式
        mBadgeGravity = typedArray.getInt(R.styleable.Badge_badge_gravity, GRAVITY_TOP or GRAVITY_RIGHT)
        //角标背景
        mBadgeBackground = typedArray.getDrawable(R.styleable.Badge_badge_background)
        //无背景则设置默认红色圆角背景
        if (mBadgeBackground == null) {
            val gradientDrawable = GradientDrawable()
            gradientDrawable.setColor(Color.RED)
            gradientDrawable.cornerRadius = 9999f
            mBadgeBackground = gradientDrawable
        }
        //角标内边距
        val badgePadding = typedArray.getDimension(R.styleable.Badge_badge_padding, -1f)
        //角标内左边距，默认值4dp
        mBadgePaddingLeft = typedArray.getDimension(R.styleable.Badge_badge_paddingLeft, if (badgePadding < 0) 4 * mDensityDP else badgePadding)
        //角标内上边距，默认值2dp
        mBadgePaddingTop = typedArray.getDimension(R.styleable.Badge_badge_paddingTop, if (badgePadding < 0) 2 * mDensityDP else badgePadding)
        //角标内右边距，默认值4dp
        mBadgePaddingRight = typedArray.getDimension(R.styleable.Badge_badge_paddingRight, if (badgePadding < 0) 4 * mDensityDP else badgePadding)
        //角标内下边距，默认值2dp
        mBadgePaddingBottom = typedArray.getDimension(R.styleable.Badge_badge_paddingBottom, if (badgePadding < 0) 2 * mDensityDP else badgePadding)
        //角标横向偏移量，默认值0dp
        mBadgeOffsetX = typedArray.getDimension(R.styleable.Badge_badge_offsetX, 0f)
        //角标横向偏移量，默认值0dp
        mBadgeOffsetY = typedArray.getDimension(R.styleable.Badge_badge_offsetY, 0f)
        //字体大小
        mBadgeTextSize = typedArray.getDimension(R.styleable.Badge_badge_textSize, 12 * mDensitySP)
        //字体颜色
        mBadgeTextColor = typedArray.getColor(R.styleable.Badge_badge_textColor, Color.WHITE)
        //字体加粗
        mBoldTextEnable = typedArray.getBoolean(R.styleable.Badge_badge_boldText, true)
        //角标小圆点半径
        mBadgeDotRadius = typedArray.getDimension(R.styleable.Badge_badge_dotRadius, 5 * mDensityDP)
        typedArray.recycle()
        //初始化画笔工具
        initTextPaint()
        //计算行高
        measureTextHeight()
        mBadgeBackgroundBounds = Rect()
    }

    /**
     * 初始化画笔工具
     */
    private fun initTextPaint() {
        mBadgeTextPaint = TextPaint()
        mBadgeTextPaint.isDither = true //防抖动
        mBadgeTextPaint.isAntiAlias = true //抗锯齿
        mBadgeTextPaint.isLinearText = true //不要文本缓存
        mBadgeTextPaint.isSubpixelText = true //设置亚像素
        mBadgeTextPaint.color = mBadgeTextColor //设置字体颜色
        mBadgeTextPaint.textSize = mBadgeTextSize //设置字体大小
        mBadgeTextPaint.textAlign = Paint.Align.CENTER //文本居中
        mBadgeTextPaint.isFakeBoldText = mBoldTextEnable //字体加粗
    }

    /**
     * 计算行高
     */
    private fun measureTextHeight() {
        mBadgeTextFontMetrics = mBadgeTextPaint!!.fontMetrics
        mBadgeTextFontMetrics?.let {
            mBadgeTextHeight = abs(it.descent - it.ascent)
        }
    }

    /**
     * 获取角标文本
     */
    var badgeText: String?
        get() = mBadgeText
        set(badgeText) {
            if (mBadgeText == null && badgeText != null || mBadgeText != null && mBadgeText != badgeText) {
                mBadgeText = badgeText
                measureBackgroundBounds()
                if (mTargetView is BadgeView) {
                    mTargetView.refreshBadge()
                } else {
                    mTargetView.invalidate()
                }
            }
        }

    /**
     * 设置角标字体颜色
     */
    fun setBadgeTextColor(color: Int) {
        mBadgeTextColor = color
        mBadgeTextPaint!!.color = mBadgeTextColor
        mTargetView.invalidate()
    }

    /**
     * 设置角标字体大小，单位px
     *
     * @param size 必须大于0
     */
    fun setBadgeTextSize(size: Float) {
        if (size > 0 && mBadgeTextSize != size) {
            mBadgeTextSize = size
            mBadgeTextPaint!!.textSize = mBadgeTextSize
            measureTextHeight()
            measureBackgroundBounds()
            if (mTargetView is BadgeView) {
                mTargetView.refreshBadge()
            } else {
                mTargetView.invalidate()
            }
        }
    }

    /**
     * 设置是否字体加粗
     */
    fun setBadgeBoldText(boldEnable: Boolean) {
        if (mBoldTextEnable != boldEnable) {
            mBoldTextEnable = boldEnable
            mBadgeTextPaint!!.isFakeBoldText = mBoldTextEnable
            measureTextHeight()
            measureBackgroundBounds()
            if (mTargetView is BadgeView) {
                mTargetView.refreshBadge()
            } else {
                mTargetView.invalidate()
            }
        }
    }

    /**
     * 设置角标背景
     */
    fun setBadgeBackground(drawable: Drawable?) {
        mBadgeBackground = drawable
        mTargetView.invalidate()
    }

    /**
     * 设置角标背景
     */
    fun setBadgeBackgroundResource(@DrawableRes resid: Int) {
        try {
            setBadgeBackground(ResourcesCompat.getDrawable(mTargetView.resources, resid, null))
        } catch (e: Resources.NotFoundException) {
            e.printStackTrace()
        }
    }

    /**
     * 设置角标偏移量
     */
    fun setBadgeOffset(offsetX: Float, offsetY: Float) {
        if (mBadgeOffsetX != offsetX || mBadgeOffsetY != offsetY) {
            mBadgeOffsetX = offsetX
            mBadgeOffsetY = offsetY
            measureBackgroundBounds()
            if (mTargetView is BadgeView) {
                mTargetView.refreshBadge()
            } else {
                mTargetView.invalidate()
            }
        }
    }

    /**
     * 设置角标横向偏移量 角标纵向偏移量
     */
    fun setBadgeOffsetX(offsetX: Float) {
        if (mBadgeOffsetX != offsetX) {
            mBadgeOffsetX = offsetX
            measureBackgroundBounds()
            if (mTargetView is BadgeView) {
                mTargetView.refreshBadge()
            } else {
                mTargetView.invalidate()
            }
        }
    }

    /**
     * 设置角标纵向偏移量
     */
    fun setBadgeOffsetY(offsetY: Float) {
        if (mBadgeOffsetY != offsetY) {
            mBadgeOffsetY = offsetY
            measureBackgroundBounds()
            if (mTargetView is BadgeView) {
                mTargetView.refreshBadge()
            } else {
                mTargetView.invalidate()
            }
        }
    }

    /**
     * 设置角标内边距，单位px
     */
    fun setBadgePadding(padding: Float) {
        setBadgePadding(padding, padding, padding, padding)
    }

    /**
     * 设置角标内边距，单位px
     */
    fun setBadgePadding(paddingLeft: Float, paddingTop: Float, paddingRight: Float, paddingBottom: Float) {
        if (mBadgePaddingLeft != paddingLeft || mBadgePaddingTop != paddingTop || mBadgePaddingRight != paddingRight || mBadgePaddingBottom != paddingBottom) {
            mBadgePaddingLeft = paddingLeft
            mBadgePaddingTop = paddingTop
            mBadgePaddingRight = paddingRight
            mBadgePaddingBottom = paddingBottom
            measureBackgroundBounds()
            if (mTargetView is BadgeView) {
                mTargetView.refreshBadge()
            } else {
                mTargetView.invalidate()
            }
        }
    }

    /**
     * 设置角标内边距-左，单位px
     */
    fun setBadgePaddingLeft(paddingLeft: Float) {
        if (mBadgePaddingLeft != paddingLeft) {
            mBadgePaddingLeft = paddingLeft
            measureBackgroundBounds()
            if (mTargetView is BadgeView) {
                mTargetView.refreshBadge()
            } else {
                mTargetView.invalidate()
            }
        }
    }

    /**
     * 设置角标内边距-上，单位px
     */
    fun setBadgePaddingTop(paddingTop: Float) {
        if (mBadgePaddingTop != paddingTop) {
            mBadgePaddingTop = paddingTop
            measureBackgroundBounds()
            if (mTargetView is BadgeView) {
                mTargetView.refreshBadge()
            } else {
                mTargetView.invalidate()
            }
        }
    }

    /**
     * 设置角标内边距-右，单位px
     */
    fun setBadgePaddingRight(paddingRight: Float) {
        if (mBadgePaddingRight != paddingRight) {
            mBadgePaddingRight = paddingRight
            measureBackgroundBounds()
            if (mTargetView is BadgeView) {
                mTargetView.refreshBadge()
            } else {
                mTargetView.invalidate()
            }
        }
    }

    /**
     * 设置角标内边距-下，单位px
     */
    fun setBadgePaddingBottom(paddingBottom: Float) {
        if (mBadgePaddingBottom != paddingBottom) {
            mBadgePaddingBottom = paddingBottom
            measureBackgroundBounds()
            if (mTargetView is BadgeView) {
                mTargetView.refreshBadge()
            } else {
                mTargetView.invalidate()
            }
        }
    }

    /**
     * 设置小圆点半径，单位px
     *
     * @param dotRadius 必须大于0
     */
    fun setBadgeDotRadius(dotRadius: Int) {
        if (dotRadius > 0 && mBadgeDotRadius != dotRadius.toFloat()) {
            mBadgeDotRadius = dotRadius.toFloat()
            if (mBadgeText != null && mBadgeText!!.length < 1) {
                measureBackgroundBounds()
                if (mTargetView is BadgeView) {
                    mTargetView.refreshBadge()
                } else {
                    mTargetView.invalidate()
                }
            }
        }
    }

    /**
     * 返回角标对齐方式
     */
    /**
     * 设置角标对齐方式
     */
    var badgeGravity: Int
        get() = mBadgeGravity
        set(gravity) {
            if (mBadgeGravity != gravity) {
                mBadgeGravity = gravity
                measureBackgroundBounds()
                if (mTargetView is BadgeView) {
                    mTargetView.refreshBadge()
                } else {
                    mTargetView.invalidate()
                }
            }
        }

    /**
     * 在目标View的dispatchDraw方法中最后调用，绘制角标
     * 不建议在onDraw中调用，因为可能会被目标View的前景遮挡，且ViewGroup在透明背景时不会调用onDraw
     */
    fun drawBadge(canvas: Canvas) {
        if (mBadgeText == null) {
            return
        }
        //绘制背景
        if (mBadgeBackground != null) {
            mBadgeBackground!!.setBounds(mBadgeBackgroundBounds!!.left, mBadgeBackgroundBounds!!.top, mBadgeBackgroundBounds!!.right, mBadgeBackgroundBounds!!.bottom)
            mBadgeBackground!!.draw(canvas)
        }
        //绘制文本
        mBadgeText?.let {
            if (it.isNotEmpty()) { //计算文本对齐坐标
                val baseX = mBadgeBackgroundBounds!!.left + (mBadgeBackgroundBounds!!.width() + mBadgePaddingLeft - mBadgePaddingRight) / 2f
                val baseY = mBadgeBackgroundBounds!!.top + (mBadgeBackgroundBounds!!.height() + mBadgePaddingTop - mBadgePaddingBottom - mBadgeTextFontMetrics!!.bottom - mBadgeTextFontMetrics!!.top) / 2f
                canvas.drawText(it, baseX, baseY, mBadgeTextPaint)
            }
        }

    }

    /**
     * 计算角标背景尺寸
     */
    private fun measureBackgroundBounds() {
        if (mBadgeText == null) {
            return
        }
        val backgroundWidth: Int
        val backgroundHeight: Int
        //确定角标的宽高
        if (mBadgeText!!.length < 1) { //文本长度为0，只显示一个小圆点
            backgroundHeight = (2f * mBadgeDotRadius + 0.5f).toInt()
            backgroundWidth = backgroundHeight
        } else { //根据文本长度计算角标的宽高
            backgroundHeight = (mBadgePaddingTop + mBadgePaddingBottom + mBadgeTextHeight + 0.5f).toInt()
            val badgeTextWidth = mBadgeTextPaint!!.measureText(mBadgeText)
            //角标的宽不能小于高
            backgroundWidth = Math.max(backgroundHeight, (mBadgePaddingLeft + mBadgePaddingRight + badgeTextWidth + 0.5f).toInt())
        }
        val left: Int
        val top: Int
        val right: Int
        val bottom: Int
        //根据对齐方式确定左右边界
        if (mBadgeGravity and GRAVITY_LEFT == GRAVITY_LEFT) { //水平居左
            left = Math.max(0, (mBadgeOffsetX - backgroundWidth / 2f + 0.5f).toInt())
            right = left + backgroundWidth
        } else if (mBadgeGravity and GRAVITY_RIGHT == GRAVITY_RIGHT) { //水平居右
            right = Math.min(mViewWidth, (mViewWidth + mBadgeOffsetX + backgroundWidth / 2f + 0.5f).toInt())
            left = right - backgroundWidth
        } else if (mBadgeGravity and GRAVITY_CENTER == GRAVITY_CENTER) { //水平居中
            left = Math.max(0, (mViewWidth / 2f + mBadgeOffsetX - backgroundWidth / 2f + 0.5f).toInt())
            right = left + backgroundWidth
        } else { //默认水平居左
            left = Math.max(0, (mBadgeOffsetX - backgroundWidth / 2f + 0.5f).toInt())
            right = left + backgroundWidth
        }
        //根据对齐方式确定上下边界
        if (mBadgeGravity and GRAVITY_TOP == GRAVITY_TOP) { //垂直居上
            top = Math.max(0, (mBadgeOffsetY - backgroundHeight / 2f + 0.5f).toInt())
            bottom = top + backgroundHeight
        } else if (mBadgeGravity and GRAVITY_BOTTOM == GRAVITY_BOTTOM) { //垂直居下
            bottom = Math.min(mViewHeight, (mViewHeight + mBadgeOffsetY + backgroundHeight / 2f + 0.5f).toInt())
            top = bottom - backgroundHeight
        } else if (mBadgeGravity and GRAVITY_CENTER == GRAVITY_CENTER) { //垂直居中
            top = Math.max(0, (mViewHeight / 2f + mBadgeOffsetY - backgroundHeight / 2f + 0.5f).toInt())
            bottom = top + backgroundHeight
        } else { //默认垂直居上
            top = Math.max(0, (mBadgeOffsetY - backgroundHeight / 2f + 0.5f).toInt())
            bottom = top + backgroundHeight
        }
        //设置角标的边界值
        mBadgeBackgroundBounds!![left, top, right] = bottom
    }

    /**
     * 返回角标总宽
     */
    val badgeWidth: Int
        get() = mBadgeBackgroundBounds!!.width()

    /**
     * 返回角标总高
     */
    val badgeHeight: Int
        get() = mBadgeBackgroundBounds!!.height()

    companion object {
        /**
         * 角标相对目标View左对齐
         */
        const val GRAVITY_LEFT = 1
        /**
         * 角标相对目标View顶对齐
         */
        const val GRAVITY_TOP = 2
        /**
         * 角标相对目标View右对齐
         */
        const val GRAVITY_RIGHT = 4
        /**
         * 角标相对目标View底对齐
         */
        const val GRAVITY_BOTTOM = 8
        /**
         * 角标相对目标View居中
         */
        const val GRAVITY_CENTER = 16
    }

    init {
        mTargetView.addOnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
            mViewWidth = right - left
            mViewHeight = bottom - top
            measureBackgroundBounds()
        }
        //初始化变量
        initBadge(mTargetView.context, attributeSet)
        //IDE编辑模式下，显示预览效果
        if (mTargetView.isInEditMode) {
            mBadgeText = "99+"
            measureBackgroundBounds()
        }
    }
}